/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.sisdoctor.persistencia.util;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import br.com.sisdoctor.persistencia.exceptions.JPADeleteException;
import br.com.sisdoctor.persistencia.exceptions.JPAInsertException;
import br.com.sisdoctor.persistencia.exceptions.JPAQueryException;
import br.com.sisdoctor.persistencia.exceptions.JPAUpdateException;
import br.com.sisdoctor.persistencia.exceptions.JPAUtilityException;

/**
 * @author andremarllus. Classe que define o comportamento comum a todos os DAOS
 *         do sistema.
 * @param<T>
 */
public class GenericDaoImpl<T> implements GenericDao<T> {

	/**
	 * Servico central para todas as acoes de persistencia.
	 */
	/**
	 * Metodo responsavel por persistir objetos.
	 * 
	 * @param entidadeGenerica
	 *            Entidade a ser incluida.
	 * @throws JPAInsertException
	 *             Se houver erro no acesso a base de dados.
	 */
	public T save(T entidadeGenerica) throws JPAInsertException {
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		try {

			manager = utility.getEntityManager();
			utility.beginTransaction(manager);
			manager.persist(entidadeGenerica);
			manager.getTransaction().commit();
			manager.refresh(entidadeGenerica);
		} catch (Exception ex) {
			utility.rollbackTransaction(manager);
			throw new JPAInsertException("Erro ao salvar objeto", ex);
		}
		return entidadeGenerica;
	}

	/**
	 * Metodo responsavel por excluir objetos.
	 * 
	 * @param entidadeGenerica
	 *            A entidade a ser excluida.
	 * @throws JPADeleteException
	 *             Se houver erro no acesso a base de dados.
	 */
	public void delete(Object entidadeGenerica) throws JPADeleteException {
		Object id = null;
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		try {
			manager = utility.getEntityManager();
			utility.beginTransaction(manager);
			id = manager.merge(entidadeGenerica);
			manager.remove(id);
			utility.commitTransaction(manager);
		} catch (Exception ex) {
			utility.rollbackTransaction(manager);
			throw new JPADeleteException("Erro ao deletar objeto", ex);
		}
	}

	/**
	 * Metodo responsavel por alterar objetos.
	 * 
	 * @param entidadeGenerica
	 *            A entidade a ser atualizada.
	 * @throws JPAUpdateException
	 *             Se houver erro no acesso a base de dados.
	 */
	@SuppressWarnings("finally")
	public Object update(Object entidadeGenerica) throws JPAUpdateException {
		Object object = null;
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		try {
			manager = utility.getEntityManager();
			utility.beginTransaction(manager);
			object = manager.merge(entidadeGenerica);
			utility.commitTransaction(manager);
		} catch (Exception ex) {
			ex.printStackTrace();
			utility.rollbackTransaction(manager);
			throw new JPAUpdateException("erro ao atualizar objeto", ex);
		} finally {
			return object;
		}
	}

	/**
	 * Metodo para recuperar um Objeto a partir de uma chave primaria.
	 * 
	 * @param clazz
	 *            A classe da entidade.
	 * @param chave
	 *            A chave primaria da entidade.
	 * @return A entidade recupera ou Null se a mesma nao existir.
	 * @throws JPAQueryException
	 *             Se houver erro no acesso a base de dados.
	 */
	public T getObjectChave(Class<? extends T> clazz, int chave)
			throws JPAQueryException {
		T retorno = null;
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		try {
			manager = utility.getEntityManager();
			utility.beginTransaction(manager);
			retorno = manager.find(clazz, chave);
			utility.commitTransaction(manager);
		} catch (Exception ex) {
			manager.getTransaction().rollback();
			throw new JPAQueryException("Erro ao pesquisar objeto com chave",
					ex);
		}
		return retorno;
	}

	/**
	 * Metodo para listar Objetos de uma entidade.
	 * 
	 * @param consulta
	 *            A consulta pelos objetos a serem recuperados.
	 * @return A entidade recuperada ou Null se a mesma nao existir.
	 * @throws JPAQueryException
	 *             Se houver erro no acesso a base de dados.
	 */
	public List<T> listObjects(String consulta) {
		List<T> object = null;
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		try {
			manager = utility.getEntityManager();
			utility.beginTransaction(manager);
			Query query = manager.createNamedQuery(consulta);
			object = query.getResultList();
			utility.commitTransaction(manager);
		} catch (Exception e) {
			utility.rollbackTransaction(manager);
		}
		return object;
	}

	@Override
	public List<T> executeNamedQuery(String nome, Object... args) throws JPAUtilityException {
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		manager = utility.getEntityManager();
		Query query = manager.createNamedQuery(nome);
		int i = 1;
		if (args != null) {
			for (Object arg : args) {
				query.setParameter(i, arg);
				i++;
			}
		}
		return query.getResultList();
	}

	@Override
	public List<T> executeNativeQuery(String sql, Object... args) throws JPAUtilityException {
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		manager = utility.getEntityManager();
		Query query = manager.createNamedQuery(sql);
		int i = 1;
		if (args != null) {
			for (Object arg : args) {
				query.setParameter(i, arg.toString());
				i++;
			}
		}
		return query.getResultList();

	}
	
	@Override
	public T getFirstByNativeQuery(String nome, Object... args) throws JPAUtilityException {
		List<T> list = executeNamedQuery(nome, args);
		return (T) ((list.isEmpty()) ? null : list.get(0));
	}

	@Override
	public T getFirstByNamedQuery(String nome, Object... args) throws JPAUtilityException {
		List<T> list = executeNamedQuery(nome, args);
		return ((list.isEmpty()) ? null : list.get(0));
	}

	public Object getFirstByNamedQueryObject(String nome, Object... args) throws JPAUtilityException {
		EntityManager manager = null;
		JPAUtility utility = new JPAUtility();
		manager = utility.getEntityManager();
		Query query = manager.createNamedQuery(nome);
		int i = 1;
		if (args != null) {
			for (Object arg : args) {
				query.setParameter(i, arg);
				i++;
			}
		}
		return query.getSingleResult();
	}
	
}
